package edu.gmu.atelier;

import java.util.ArrayList;
import java.util.List;

/**
 * Vector defined within some vector space.
 * @author James H. Pope
 */
public class RealVector implements Vector
{
    private double[] elements = null;
    
    /**
     * Makes a new vector from data in R^2.
     * @param data (column for vector)
     */
    public RealVector(double x, double y)
    {
        this( new double[]{x,y} );
    }
    
    /**
     * Makes a new vector from data in R^3.
     * @param data (column for vector)
     */
    public RealVector(double x, double y, double z)
    {
        this( new double[]{x,y,z} );
    }
    
    /**
     * Makes a new vector from data in R^(comps.len).
     * @param data (column for vector)
     */
    public RealVector(double[] comps)
    {
        this.elements = comps;
    }
    
    //------------------------------------------------------------------------//
    // Interface methods
    //------------------------------------------------------------------------//
    public RealVector normalize()
    {
        double norm = 0.0;
        for (int i = 0; i < elements.length; i++)
        {
            norm += elements[i] * elements[i];
        }
        double normInv = 1.0 / Math.sqrt(norm);
        
        double[] elems = new double[this.elements.length];
        for (int i = 0; i < elems.length; i++)
        {
            elems[i] = this.elements[i] * normInv;
        }
        return new RealVector(elems);
    }
    
    public Element norm2()
    {
        double norm = 0.0;
        for (int i = 0; i < elements.length; i++)
        {
            norm += elements[i] * elements[i];
        }
        return new RealElement(norm);
    }
    
    public Vector add(Vector v)
    {
        double[] elems = new double[this.elements.length];
        for (int i = 0; i < elements.length; i++)
        {
            elems[i] = elements[i] + v.getElement(i).doubleValue();
        }
        return new RealVector(elems);
    }
    
    public Vector sub(Vector v)
    {
        double[] elems = new double[this.elements.length];
        for (int i = 0; i < elements.length; i++)
        {
            elems[i] = elements[i] - v.getElement(i).doubleValue();
        }
        return new RealVector(elems);
    }
    
    public Vector mult(Element e)
    {
        double[] elems = new double[this.elements.length];
        double s = e.doubleValue();
        for (int i = 0; i < elements.length; i++)
        {
            elems[i] = elements[i] * s;
        }
        return new RealVector(elems);
    }
    
    public Element dot(Vector v )
    {
        RealVector rv = (RealVector)v;
        double dot = 0.0;
        for (int i = 0; i < elements.length; i++)
        {
            dot += elements[i]*rv.elements[i];
        }
        return new RealElement(dot);
    }
    
    public Element getElement(int i)
    {
        return new RealElement( this.elements[i] );
    }
    
    public void setElement(int i, Element e)
    {
        this.elements[i] = e.doubleValue();
    }
    
    public int getLength()
    {
        return this.elements.length;
    }
    
    public RealVector mult( IMatrix a )
    {
        java.util.List<Vector> vectors = a.getVectors();
        double[] x = this.elements;
        double[] b = new double[a.getRows()];
        for (int i = 0; i < x.length; i++)
        {
            RealVector v = (RealVector)vectors.get(i);
            double[] vi = v.elements;
            double xWeight = x[i];
            for (int j = 0; j < vi.length; j++)
            {
                b[j] += xWeight * vi[j];
            }
            
        }
        return new RealVector(b);
    }
    
    /**
     * Returns true is all the elements are the zero element.
     * @param
     */
    public boolean isZero()
    {
        for (int i = 0; i < elements.length; i++)
        {
            if( elements[i] != 0.0 )
            {
                return false;
            }
        }
        return true;
    }
    
    public RealVector copy()
    {
        double[] elems = new double[this.elements.length];
        for (int i = 0; i < elems.length; i++)
        {
            elems[i] = this.elements[i];
        }
        return new RealVector(elems);
    }
    
    public RealVector copy(int start, int length)
    {
        double[] elems = new double[length];
        for (int i = 0; i < elems.length; i++)
        {
            elems[i] = this.elements[start+i];
        }
        return new RealVector(elems);
    }
    
    public void copyElements(int start, Vector v)
    {
        RealVector rv = (RealVector)v;
        for (int r = 0; r < rv.elements.length; r++)
        {
            elements[start+r] = rv.elements[r];
        }
    }
    
    public Matrix transpose( Matrix a )
    {
        List<Vector> vectors = a.getVectors();
        List t = new ArrayList<Vector>();
        int n = vectors.size();
        int m = vectors.get(0).getLength();
        
        for (int i = 0; i < m; i++)
        {
            double[] c = new double[n];
            for (int j = 0; j < n; j++)
            {
                c[j] = ((RealVector)vectors.get(j)).elements[i];
            }
            t.add( new RealVector(c) );
        }
        return new Matrix(t, a.getFactory());
    }
    
    public Vector extract( int[] B )
    {
        double[] es = new double[B.length];
        for (int i = 0; i < es.length; i++)
        {
            es[i] = this.elements[B[i]];
        }
        return new RealVector(es);
    }
    
    public void extractTo( int[] B, Vector b )
    {
        RealVector v = (RealVector)b;
        for (int i = 0; i < B.length; i++)
        {
            v.elements[i] = this.elements[B[i]];
        }
    }
    
    
    //------------------------------------------------------------------------//
    // Vector specific "row" mutator operations
    //------------------------------------------------------------------------//
    public void swap( int fromRow, int toRow )
    {
        double temp = this.elements[fromRow];
        this.elements[fromRow] = this.elements[toRow];
        this.elements[toRow] = temp;
    }
    
    public void mulAddTo( int fromRow, Element mult, int toRow )
    {
        this.elements[toRow] += mult.doubleValue() * this.elements[fromRow];
    }
    
    public void mulTo( int row, Element mult )
    {
        this.elements[row] *= mult.doubleValue();
    }
    
    
    //------------------------------------------------------------------------//
    // Class instance methods
    //------------------------------------------------------------------------//
    public boolean similar( Object obj, Epsilon e )
    {
        if(obj == this)
        {
            return true;
        }
        
        if( (obj instanceof RealVector) == false )
        {
            return false;
        }
        
        RealVector o = (RealVector)obj;
        
        if( o.elements.length != this.elements.length )
        {
            return false;
        }
        
        if( e != null )
        {
            for (int i = 0; i < this.elements.length; i++)
            {
                // Need epsilon check here
                //if( this.components[i] != o.components[i] )
                if( e.similar( this.elements[i], o.elements[i] ) == false )
                {
                    return false;
                }
            }
        }
        else
        {
            for (int i = 0; i < this.elements.length; i++)
            {
                // Exact check, no epsilon specified
                if( this.elements[i] != o.elements[i] )
                {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    public boolean equals( Object obj )
    {
        return this.similar(obj, null);
    }
    
    public String toString()
    {
        StringBuilder buf = new StringBuilder("[");
        for (int i = 0; i < this.elements.length; i++)
        {
            buf.append( this.elements[i] );
            buf.append( "," );
        }
        buf.append("]");
        return buf.toString();
    }
}
